package main

import (
	"bufio"
	"errors"
	"io"
	"regexp"
	"strconv"
	"strings"
	"time"
)

//ValueType --
type ValueType int

//List --
//Object --
const (
	TABLE ValueType = iota
	LEAF
	KNOT ///
)

//Tree --
type Tree struct {
	Value interface{}
	Vtype ValueType
}

func (t *Tree) object() *Object {
	if t.Vtype == KNOT || t.Vtype == LEAF {
		return t.Value.(*Object)
	} else if t.Vtype == TABLE {
		tb := t.Value.(Table)
		return tb[len(tb)-1]
	}
	return nil
}

func (t *Tree) table() Table {
	if t.Vtype == TABLE {
		return t.Value.(Table)
	}
	return nil
}

//Object --
type Object struct {
	m    map[string]interface{}
	Keys []string
}

func NewObject() *Object {
	return &Object{m: make(map[string]interface{}), Keys: []string{}}
}
func (o *Object) Add(key string, value interface{}) {
	if _, ok := o.m[key]; !ok {
		o.Keys = append(o.Keys, key)
	}
	o.m[key] = value
}
func (o *Object) Value(key string) interface{} {
	return o.m[key]
}

//GetString --
func (o *Object) GetString(key string) string {
	if cc, ok := o.m[key]; ok {
		if str, ok := cc.(string); ok {
			return str
		}
	}
	return ""
}

//GetFloat --
func (o *Object) GetFloat(key string) (float64, error) {
	str := o.GetString(key)
	if len(str) > 0 {
		return strconv.ParseFloat(str, 32)
	}
	return 0, nil
}

func (o *Object) GetDateTime(key string, timeLayout string) (time.Time, error) {
	str := o.GetString(key)
	if len(str) > 0 {
		return time.Parse(timeLayout, str)
	}
	return time.Time{}, errors.New("bad value for field")
}

//GetOject --
func (o *Object) GetOject(path ...string) (child *Object) {

	child = o
	for _, p := range path {
		if cc, ok := child.m[p]; ok {
			if nd, ok := cc.(*Tree); ok {
				child = nd.object()
			} else {
				return nil
			}
		} else {
			return nil
		}
	}
	return
}

//GetTable --
func (o *Object) GetTable(path ...string) (child Table) {
	if len(path) == 0 {
		return nil
	}

	p := o
	for i, s := range path {
		if cc, ok := p.m[s]; ok {
			if nd, ok := cc.(*Tree); ok {
				if len(path)-1 == i {
					return nd.table()
				} else {
					p = nd.object()
				}
			} else {
				return nil
			}
		} else {
			return nil
		}
	}
	return
}

//GetStringArray --
func (o *Object) GetStringArray(key string) []string {

	str := o.GetString(key)
	if len(str) > 0 && str[0] == '[' && str[len(str)-1] == ']' {
		list := strings.Split(str[1:len(str)-2], ",")
		for i, str := range list {
			list[i] = strings.Trim(str, "\" ")
		}
		return list
	}
	return nil
}

//GetFloatArray --
func (o *Object) GetFloatArray(key string) []float64 {

	v := o.GetString(key)
	if len(v) > 0 && v[0] == '[' && v[len(v)-1] == ']' {
		strs := strings.Split(v[1:len(v)-2], ",")
		list := []float64{}
		for _, str := range strs {
			str = strings.TrimSpace(str)
			if len(str) > 0 {
				if f, err := strconv.ParseInt(str, 10, 64); err == nil {
					list = append(list, float64(f))
				} else if f, err := strconv.ParseFloat(str, 32); err == nil {
					list = append(list, f)
				}
			}
		}
		return list
	}
	return nil
}

//Table --
type Table []*Object

//NewTOMLTree --
func NewTOMLTree(rd io.Reader) (root *Object) {

	root = NewObject()
	current := root

	var s = bufio.NewScanner(rd)
	s.Split(bufio.ScanLines)
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		if len(line) > 0 && line[0] != '#' {

			//[Leaf]
			reg := regexp.MustCompile(`^\[([\S ]+)\]$`)
			group := reg.FindStringSubmatch(line)
			if len(group) == 2 {
				p := root
				gi := strings.Split(group[1], ".")
				for i, g := range gi {
					pv := p
					g = strings.TrimSpace(g)
					if len(g) > 0 {
						if v, ok := pv.m[g]; !ok {
							tmp := &Tree{NewObject(), KNOT}
							if len(gi)-1 == i {
								tmp.Vtype = LEAF
							}
							pv.Add(g, tmp)
							p = tmp.object()
						} else {
							if len(gi)-1 == i {
								if me, ok := v.(*Tree); ok {

									if me.Vtype == KNOT {
										me.Vtype = LEAF
									} else if me.Vtype == LEAF {
										me.Vtype = TABLE
										me.Value = Table{me.Value.(*Object), NewObject()}
									} else if me.Vtype == TABLE {
										me.Value = append(me.Value.(Table), NewObject())
									}
								} else {
									panic("节点名存在冲突")
								}
							}
							p = v.(*Tree).object()
						}
					}

					current = p
				}
				continue
			}

			//Key = Value
			reg = regexp.MustCompile(`^(.+?)=(.*)$`)
			kv := reg.FindStringSubmatch(line)
			if len(kv) == 3 {
				//key
				key := strings.TrimSpace(kv[1])
				value := strings.Trim(strings.TrimSpace(strings.Split(kv[2], "#")[0]), "\"")
				obj := current
				obj.Add(key, value)
			}
		}
	}
	return
}
